/**
 * Copyright (c) 2007, Blue Hole Software. All rights reserved.
 * Code licensed under the Apache 2.0 License:
 * http://www.apache.org/licenses/
 */
package org.bhf.providers.security.authentication;

import org.bhf.json.JSONTransient;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;

/**
 * Model for a User.
 */
@Entity
@Table( name = "USERS" )
@NamedQueries
({
  @NamedQuery( name="User.byLoginID", query="select u from User u where u.loginID = :loginID" )
})
public class User
{
    private String          id;
    private String          lastName;
    private String          firstName;
    private String          nickName;
    private Locale          language        = Locale.getDefault();
    private TimeZone        timeZone        = TimeZone.getDefault();
    private String          email;
    private String          group;
    private Date            created         = new Date();
    private Set<RoleMap>    roleMap         = new HashSet<RoleMap>();

    private String          loginID;
    private String          passwordHash;
    private boolean         disabled        = false;
    private Date            expires         = new Date();
    private Date            changed         = new Date();
    private Date            lastLogin       = new Date(0);
    private long            holdExpiration  = 0;

    private String          extension;

    @Id
    public String                   getId()
    {
        return id;
    }

    public void                     setId( String id )
    {
        this.id = id;
    }

    public long                     getHoldExpiration()
    {
        return holdExpiration;
    }

    public void                     setHoldExpiration( final long holdExpiration )
    {
        this.holdExpiration = holdExpiration;
    }

    @Column( name = "USER_GROUP", nullable = false )
    public String                   getGroup()
    {
        return group;
    }

    public void                     setGroup( String group )
    {
        this.group = group;
    }

    @OneToMany( cascade = {CascadeType.ALL} )
    @JSONTransient
    public Set<RoleMap>             getRoleMap()
    {
        return roleMap;
    }

    public void                     setRoleMap( final Set<RoleMap> roleMap )
    {
        this.roleMap = roleMap;
    }

    @Lob
    public String                   getExtension()
    {
        return extension;
    }

    public void                     setExtension( final String extension )
    {
        this.extension = extension;
    }

    @Column( nullable = false )
    public String                   getLastName()
    {
        return lastName;
    }

    public void                     setLastName( final String lastName )
    {
        this.lastName = lastName;
    }

    @Column( nullable = false )
    public String                   getFirstName()
    {
        return firstName;
    }

    public void                     setFirstName( final String firstName )
    {
        this.firstName = firstName;
    }

    @Column( nullable = false )
    public String                   getNickName()
    {
        return nickName;
    }

    public void                     setNickName( final String nickName )
    {
        this.nickName = nickName;
    }

    /**
     * @return String representation of the <code>Locale</code>
     * @exclude
     */
    @Column( name = "LOCALE", nullable = false )
    public String                   getPesistentLanguage()
    {
        return language.getLanguage() + ":" + language.getCountry() + ":" + language.getVariant();
    }

    /**
     * @param localeSpecification String representation of the <code>Locale</code>
     * @exclude
     */
    public void                     setPesistentLanguage( final String localeSpecification )
    {
        final String[] spec = localeSpecification.split( ":" );

        switch( spec.length )
        {
            case 1: language = new Locale( spec[0] );                   break;
            case 2: language = new Locale( spec[0], spec[1] );          break;
            case 3: language = new Locale( spec[0], spec[1], spec[2] ); break;
            default:
                throw new RuntimeException( "Bad locale specification: " + localeSpecification );
        }
    }

    @Transient
    public Locale                   getLanguage()
    {
        return language;
    }

    public void                     setLanguage( final Locale language )
    {
        this.language = language;
    }

    /**
     * @return String representation of the <code>TimeZone</code>
     * @exclude
     */
    @Column( name = "TIME_ZONE", nullable = false )
    public String                   getPersistentTimeZone()
    {
        return timeZone.getID();
    }

    /**
     * @param timeZoneSpecification String representation of the <code>TimeZone</code>
     * @exclude
     */
    public void                     setPersistentTimeZone( final String timeZoneSpecification )
    {
        this.timeZone = TimeZone.getTimeZone( timeZoneSpecification );
    }

    @Transient
    public TimeZone                 getTimeZone()
    {
        return timeZone;
    }

    public void                     setTimeZone( final TimeZone timeZone )
    {
        this.timeZone = timeZone;
    }

    public String                   getEmail()
    {
        return email;
    }

    public void                     setEmail( final String email )
    {
        this.email = email;
    }

    @Temporal( TemporalType.TIMESTAMP )
    @Column( nullable = false )
    public Date                     getCreated()
    {
        return created;
    }

    public void                     setCreated( final Date created )
    {
        this.created = created;
    }

    //
    // Credentials
    //

    public String                   getLoginID()
    {
        return loginID;
    }

    public void                     setLoginID( final String loginID )
    {
        this.loginID = loginID;
    }

    @JSONTransient
    public String                   getPasswordHash()
    {
        return passwordHash;
    }

    public void                     setPasswordHash( final String passwordHash )
    {
        this.passwordHash = passwordHash;
    }

    public boolean                  isDisabled()
    {
        return disabled;
    }

    public void                     setDisabled( final boolean disabled )
    {
        this.disabled = disabled;
    }

    @Temporal( TemporalType.TIMESTAMP )
    public Date                     getExpires()
    {
        return expires;
    }

    public void                     setExpires( final Date expires )
    {
        this.expires = expires;
    }

    @Temporal( TemporalType.TIMESTAMP )
    public Date                     getLastLogin()
    {
        return lastLogin;
    }

    public void                     setLastLogin( final Date lastLogin )
    {
        this.lastLogin = lastLogin;
    }

    @Temporal( TemporalType.TIMESTAMP )
    public Date                     getChanged()
    {
        return changed;
    }

    public void                     setChanged( final Date changed )
    {
        this.changed = changed;
    }

    //
    // Strike out hold
    //

    @Transient
    public boolean                  isOnHold()
    {
        return System.currentTimeMillis() < holdExpiration;
    }

    public void                     placeOnHoldFor( final int seconds )
    {
        holdExpiration = System.currentTimeMillis() + seconds * 1000L;
    }

    //
    // Queries
    //

    public static User findByLoginID( final EntityManager em, final String loginID )
    {
        final Query q = em.createNamedQuery( "User.byLoginID" );
        q.setParameter( "loginID", loginID );
        return (User)q.getSingleResult();
    }
}
